Skip to content

RFC 4771 ROC in-band support#814

Open
volovolov wants to merge 4 commits into
cisco:mainfrom
volovolov:merging_rfc4771
Open

RFC 4771 ROC in-band support#814
volovolov wants to merge 4 commits into
cisco:mainfrom
volovolov:merging_rfc4771

Conversation

@volovolov

Copy link
Copy Markdown

Add RFC 4771 Roll-over Counter Carrying (RCC) support — modes 1, 2 and 3

Summary

This PR implements the RFC 4771 Roll-over Counter (ROC) Carrying integrity transform for SRTP. It lets a sender periodically embed its 32-bit ROC in the SRTP authentication tag of selected packets, so a late-joining or re-synchronizing receiver can recover the correct ROC without having to guess it from the RTP sequence number. All three RFC 4771 modes are supported, with mode 3 layered on top of the AES-GCM AEAD transform per RFC 7714 §8.2.

RCC is opt-in and fully backward compatible: streams that do not enable it (srtp_rcc_mode_none, the default) behave exactly as before and use the standard RFC 3711 implicit-ROC transform.

Motivation

Under RFC 3711 the ROC is never transmitted; a receiver infers it from the RTP sequence number. A receiver that joins mid-stream, loses synchronization, or misses a sequence-number wrap can end up with the wrong ROC and fail authentication on every subsequent packet. RFC 4771 solves this by carrying the ROC in-band on a configurable subset of packets ("ROC-carrying" packets, those whose RTP sequence number is ≡ 0 (mod R)).

What's implemented

  • Mode 1 — Only ROC-carrying packets are integrity-protected; other packets carry no authentication tag.
  • Mode 2 — ROC-carrying packets use the RCC tag; all other packets use the standard integrity transform. (Most common / robust choice.)
  • Mode 3 — NULL-MAC variant: the transform carries only the 4-octet ROC with no MAC of its own. Supported on top of AES-GCM (RFC 7714), where the AEAD tag authenticates the packet and the ROC feeds the GCM IV.

For a ROC-carrying packet in modes 1/2, the tag is built as:

TAG = ROC (4 octets) || MAC_tr

where MAC_tr is the (auth_tag_len - 4) most-significant octets of the HMAC-SHA1. This requires auth_tag_len >= 5.

Packet layout

Modes 1/2 (AES-CM + HMAC-SHA1), ROC-carrying packet:

[ RTP header ][ encrypted payload ][ MKI (optional) ][ ROC(4) || MAC_tr ]

Mode 3 (AES-GCM, RFC 7714 §8.2), ROC-carrying packet:

[ RTP header ][ ciphertext incl. 16-octet GCM tag ][ MKI (optional) ][ ROC(4) ]

The 4-octet ROC is the SRTP authentication-tag field and is placed after the MKI. It is not part of the GCM AAD (AAD remains the RTP header only); instead the ROC feeds the GCM IV, so any tampering with the carried ROC produces a wrong IV and a GCM authentication failure — giving implicit integrity protection of the ROC.

API changes

New public enum and function in include/srtp.h:

typedef enum {
    srtp_rcc_mode_none = 0,   /* default RFC 3711 transform */
    srtp_rcc_mode_1    = 1,
    srtp_rcc_mode_2    = 2,
    srtp_rcc_mode_3    = 3
} srtp_rcc_mode_t;

srtp_err_status_t srtp_policy_set_rcc_mode_tx_rate(
    srtp_policy_t policy, srtp_rcc_mode_t rcc_mode, uint16_t roc_tx_rate);
  • roc_tx_rate (R) is the transmission rate; R == 1 carries the ROC in every packet. Must be >= 1 when RCC is enabled.
  • Both peers must derive the same mode and rate so they agree on the packet layout.
  • Validation enforces cipher/mode consistency: modes 1/2 require an AES-CM profile and auth_tag_len >= 5; mode 3 requires an AES-GCM profile. Inconsistent combinations are rejected by srtp_create() / policy validation.

Implementation notes

  • Modes 1/2 are handled by dedicated srtp_protect_rcc() / srtp_unprotect_rcc() paths in srtp/srtp.c.
  • Mode 3 is handled inside srtp_protect_aead() / srtp_unprotect_aead(), since GCM dispatch happens ahead of the RCC dispatch.
  • On a ROC-carrying packet the receiver adopts the sender's ROC (srtp_rdbx_set_roc_seq() + re-anchoring the replay index); non-carrying packets use the normal estimated packet index and replay check.
  • MKI lookup: because the ROC now follows the MKI in mode 3, the unprotect dispatch passes srtp_len - 4 for ROC-carrying packets so the existing "MKI is last" lookup still locates the MKI correctly. Carry-packet detection uses only seq % R == 0 and the configured mode (no session keys required).

Testing

Added test/rcc_test.c covering:

  • Modes 1 and 2 round-trip protect/unprotect (AES-CM + HMAC-SHA1).
  • Mode 3 round-trip over AES-GCM.
  • ROC recovery on a receiver starting from a mismatched ROC.
  • Tamper detection for mode 3 (modifying the carried ROC yields srtp_err_status_auth_fail).

All tests pass. Mode 3 tests require a build with GCM enabled (-DENABLE_OPENSSL=ON).

Backward compatibility

  • Default behavior is unchanged: srtp_rcc_mode_none streams use the standard RFC 3711 transform.
  • No changes to existing wire format for non-RCC streams.
  • New API is purely additive.

Limitations / open items

  • Modes 1/2 are defined only for AES-CM ciphers; mode 3 only for AES-GCM (matching the RFCs).
  • RCC applies to SRTP (RTP); SRTCP is unaffected.

@pabuhler

pabuhler commented Jul 7, 2026

Copy link
Copy Markdown
Member

@volovolov , if you rebase on main then the wolfssl build failures should be fixed.

@volovolov

Copy link
Copy Markdown
Author

@volovolov , if you rebase on main then the wolfssl build failures should be fixed.

@pabuhler Thank you, it's done now!

@pabuhler

pabuhler commented Jul 7, 2026

Copy link
Copy Markdown
Member

@volovolov , looks like something else broke :( ... it is at least not related to your changes, I will follow it up

@pabuhler pabuhler left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an initial review, have not fully reviewed the main change yet.

Comment thread test/rcc_test.c
@@ -0,0 +1,605 @@
/*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great that you have added these tests, some feedback though.

This test is not run any where, it needs to be added as a test target for at least cmake builds, but ideally also autotools and mason.

The style of tests in this file does not match either srtp_driver.c or test_srtp_policy.c , it would be good to not add a new test style. My preference would be to do it in the same way as test_srtp_policy.c

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to do all of that, please review the latest changes in rcc_test.c

Comment thread srtp/srtp.c Outdated
}
}

/*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a duplicate block of code, one should be deleted.
This validation should also maybe be done as part of srtp_policy_validate().

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, makes sense indeed to move the validation into srtp_policy_validate(), did that.

Comment thread srtp/srtp_policy.c
if (policy == NULL) {
return srtp_err_status_bad_param;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should you check that roc_tx_rate >= 1 when mode != none ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, done now.

@volovolov
volovolov requested a review from pabuhler July 9, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants